﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Xml;
using DiscoveryLogic.Common.Utilities;
using EntityType=DiscoveryLogic.DataViz.NetworkDiagram.NetworkData.EntityType;

namespace DiscoveryLogic.DataViz.NetworkDiagram.Rules
{
    /// <summary>
    /// 
    /// </summary>
    public struct ArgumentFieldBinding
    {
        public string ArgumentName;
        public bool FromSrcEntity;
        public bool FromTgtEntity;
        public string PropertyName;
    }

    /// <summary>
    /// 
    /// </summary>
    public class EntityRelationRule
    {
        #region props
        private string _RuleName;
        /// <summary></summary>
        public string RuleName
        {
            get
            {
                return this._RuleName;
            }
            set
            {
                this._RuleName = value;
            }
        }

        private Type _SrcEntityType;
        /// <summary></summary>
        public Type SrcEntityType
        {
            get
            {
                return this._SrcEntityType;
            }
            set
            {
                this._SrcEntityType = value;
            }
        }

        private string _SrcEntityIDField;
        /// <summary></summary>
        public string SrcEntityIDField
        {
            get
            {
                return this._SrcEntityIDField;
            }
            set
            {
                this._SrcEntityIDField = value;
            }
        }

        private string _SrcEntityNameField;
        /// <summary></summary>
        public string SrcEntityNameField
        {
            get
            {
                return this._SrcEntityNameField;
            }
            set
            {
                this._SrcEntityNameField = value;
            }
        }

        private Type _TgtEntityType;
        /// <summary></summary>
        public Type TgtEntityType
        {
            get
            {
                return this._TgtEntityType;
            }
            set
            {
                this._TgtEntityType = value;
            }
        }

        private string _TgtEntityIDField;
        /// <summary></summary>
        public string TgtEntityIDField
        {
            get
            {
                return this._TgtEntityIDField;
            }
            set
            {
                this._TgtEntityIDField = value;
            }
        }

        private string _TgtEntityNameField;
        /// <summary></summary>
        public string TgtEntityNameField
        {
            get
            {
                return this._TgtEntityNameField;
            }
            set
            {
                this._TgtEntityNameField = value;
            }
        }

        private DataProviderType _DbProvider;
        /// <summary></summary>
        public DataProviderType DbProvider
        {
            get
            {
                return this._DbProvider;
            }
            set
            {
                this._DbProvider = value;
            }
        }

        private string _ConnStr;
        /// <summary></summary>
        public string ConnStr
        {
            get
            {
                return this._ConnStr;
            }
            set
            {
                this._ConnStr = value;
            }
        }

        private string _Sql;
        /// <summary></summary>
        public string Sql
        {
            get
            {
                return this._Sql;
            }
            set
            {
                this._Sql = value;
            }
        }

        #region input binding
        private Dictionary<string, string> _FieldNameMappings;
        /// <summary>
        /// field name from source data --> PropertyName
        /// </summary>
        public Dictionary<string, string> FieldNameMappings
        {
            get
            {
                return this._FieldNameMappings;
            }
            set
            {
                this._FieldNameMappings = value;
            }
        }
        #endregion

        #region sql binding
        private Dictionary<string,string> _SqlParameters;
        /// <summary>
        /// property name --> parameter name
        /// </summary>
        public Dictionary<string,string> SqlParameters
        {
            get
            {
                return this._SqlParameters;
            }
            set
            {
                this._SqlParameters = value;
            }
        }

        private Dictionary<string, SqlDbType> _parameterTypes;
        /// <summary>
        /// SqlParameterName (select from property names) --> SqlDbType
        /// </summary>
        public Dictionary<string, SqlDbType> ParameterTypes
        {
            get
            {
                return this._parameterTypes;
            }
            set
            {
                this._parameterTypes = value;
            }
        }

        private Dictionary<string, string> _FieldTransformations;
        /// <summary>
        /// transform field value before bind to sql parameter
        /// property name --> code
        /// </summary>
        public Dictionary<string, string> FieldTransformations
        {
            get
            {
                return this._FieldTransformations;
            }
            set
            {
                this._FieldTransformations = value;
            }
        }
        #endregion

        #region output binding
        private Dictionary<string, string> _OutputFieldNameMappings;
        /// <summary>
        /// output table column name --> target entity field name
        /// </summary>
        public Dictionary<string, string> OutputFieldNameMappings
        {
            get
            {
                return this._OutputFieldNameMappings;
            }
            set
            {
                this._OutputFieldNameMappings = value;
            }
        }

        #endregion

        #region entity link 
        private Dictionary<string, ArgumentFieldBinding> _LinkCalcArgumentMappings;
        /// <summary>
        /// argument name --> ArgumentFieldBinding
        /// </summary>
        public Dictionary<string, ArgumentFieldBinding> LinkCalcArgumentMappings
        {
            get
            {
                return this._LinkCalcArgumentMappings;
            }
            set
            {
                this._LinkCalcArgumentMappings = value;
            }
        }

        private Type _entityLinkerType;
        /// <summary></summary>
        public Type EntityLinkerType
        {
            get
            {
                return this._entityLinkerType;
            }
            set
            {
                this._entityLinkerType = value;
            }
        }

        private double _LinkDistanceThreshold;
        /// <summary>
        /// distance in the range of [0, infinite)
        /// the shorter the distance, more closely they are related
        /// </summary>
        public double LinkDistanceThreshold
        {
            get
            {
                return this._LinkDistanceThreshold;
            }
            set
            {
                this._LinkDistanceThreshold = value;
            }
        }

        private bool _AllowLoopBack;
        /// <summary>
        /// in the case of finding relation: Person -> Organization 
        /// found Person (a1) -> 3 Organizations (b1, b2, b3)
        /// when allow loop back, the exploration continues in the following steps
        /// 1. b1->(a1,a2,a3), b2->(a1,a4,a5), b3->(a1, a6)
        /// 2. if distance(a1->b1->a2) less then threshold, a2->(b1,b4)
        /// </summary>
        public bool AllowLoopBack
        {
            get
            {
                return this._AllowLoopBack;
            }
            set
            {
                this._AllowLoopBack = value;
            }
        }

        private double _HopDampFactor;
        /// <summary>
        /// this is used in distance filter
        /// suppose A->B->C->D with distance (0.1, 0.5, 0.6)
        /// when damp factor = 0.5, hop distance scale factor = 1/0.5=2
        /// distance from A to B = 0.1 * 2 * 1 = 0.2
        /// distance from A to C = 0.1 * 2 + 0.5 * 2 * 2 = 2.2
        /// distance from A to D = 0.1 * 2 * 1 + 0.5 * 2 * 2 + 0.6 * 2 * 3 = 5.8
        /// </summary>
        public double HopDampFactor
        {
            get
            {
                return this._HopDampFactor;
            }
            set
            {
                this._HopDampFactor = value;
            }
        }
        #endregion

        #region dedup
        private Type _DedupCalculatorType;
        /// <summary></summary>
        public Type DedupCalculatorType
        {
            get
            {
                return this._DedupCalculatorType;
            }
            set
            {
                this._DedupCalculatorType = value;
            }
        }

        private Dictionary<string,string> _DedupNativeComparers;
        /// <summary></summary>
        public Dictionary<string,string> DedupNativeComparers
        {
            get
            {
                return this._DedupNativeComparers;
            }
            set
            {
                this._DedupNativeComparers = value;
            }
        }

        private Dictionary<string,string> _DedupAssociativeComparers;
        /// <summary></summary>
        public Dictionary<string,string> DedupAssociativeComparers
        {
            get
            {
                return this._DedupAssociativeComparers;
            }
            set
            {
                this._DedupAssociativeComparers = value;
            }
        }

        private Dictionary<string,double> _DedupFieldWeights;
        /// <summary></summary>
        public Dictionary<string,double> DedupFieldWeights
        {
            get
            {
                return this._DedupFieldWeights;
            }
            set
            {
                this._DedupFieldWeights = value;
            }
        }

        private Dictionary<string,double> _DedupPenalties;
        /// <summary></summary>
        public Dictionary<string,double> DedupPenalties
        {
            get
            {
                return this._DedupPenalties;
            }
            set
            {
                this._DedupPenalties = value;
            }
        }

        private double _DedupAssociationThreshold;
        /// <summary></summary>
        public double DedupAssociationThreshold
        {
            get
            {
                return this._DedupAssociationThreshold;
            }
            set
            {
                this._DedupAssociationThreshold = value;
            }
        }

        private double _DedupSimilarityThreshold;
        /// <summary>
        /// Similarity in the range of [0,1], including undefined
        /// 1 means they are the same with 100% confidence
        /// </summary>
        public double DedupSimilarityThreshold
        {
            get
            {
                return this._DedupSimilarityThreshold;
            }
            set
            {
                this._DedupSimilarityThreshold = value;
            }
        }
        #endregion

        #endregion

        #region xml
        /// <summary>
        /// 
        /// </summary>
        /// <param name="xDoc"></param>
        /// <param name="relationRuleNode"></param>
        public void Save(ref XmlDocument xDoc, XmlNode relationRuleNode)
        {
            relationRuleNode.RemoveAll();
            XmlDataUtil.UpdateAttribute(ref xDoc, relationRuleNode, "RuleName",this.RuleName);
            XmlDataUtil.UpdateAttribute(ref xDoc, relationRuleNode,"SrcEntityType", this.SrcEntityType.FullName);
            XmlDataUtil.UpdateAttribute(ref xDoc, relationRuleNode, "SrcIDField",this.SrcEntityIDField);
            XmlDataUtil.UpdateAttribute(ref xDoc, relationRuleNode, "SrcNameField", this.SrcEntityNameField);
            XmlDataUtil.UpdateAttribute(ref xDoc, relationRuleNode,"TgtEntityType",this.TgtEntityType.FullName);
            XmlDataUtil.UpdateAttribute(ref xDoc, relationRuleNode, "TgtIDField",this.TgtEntityIDField);
            XmlDataUtil.UpdateAttribute(ref xDoc, relationRuleNode, "TgtNameField",this.TgtEntityNameField);
            XmlDataUtil.UpdateAttribute(ref xDoc, relationRuleNode, "DbProvider",this.DbProvider.ToString());
            XmlDataUtil.UpdateAttribute(ref xDoc, relationRuleNode, "ConnStr",this.ConnStr);
            XmlDataUtil.UpdateAttribute(ref xDoc, relationRuleNode, "Sql",this.Sql);
            
            if(this.FieldNameMappings !=null && this.FieldNameMappings.Count>0)
            {
                XmlNode inputMappingNodes = XmlDataUtil.AddElement(ref xDoc, relationRuleNode, "InputMappings");
                foreach(string propName in this.FieldNameMappings.Keys)
                {
                    XmlNode inputMappingNode = XmlDataUtil.AddElement(ref xDoc, inputMappingNodes, "InputMapping");
                    XmlDataUtil.UpdateAttribute(ref xDoc, inputMappingNode, "SrcField",this.FieldNameMappings[propName]);
                    XmlDataUtil.UpdateAttribute(ref xDoc, inputMappingNode, "PropName",propName);
                }
            }

            if(this.SqlParameters !=null && this.SqlParameters.Count>0)
            {
                XmlNode parameterNodes = XmlDataUtil.AddElement(ref xDoc, relationRuleNode, "SqlParameters");
                foreach(string propName in this.SqlParameters.Keys)
                {
                    XmlNode parameterNode = XmlDataUtil.AddElement(ref xDoc, parameterNodes, "SqlParameter");
                    XmlDataUtil.UpdateAttribute(ref xDoc, parameterNode, "PropertyName",propName);
                    XmlDataUtil.UpdateAttribute(ref xDoc, parameterNode, "ParameterName",this.SqlParameters[propName]);
                }
            }

            if (this.ParameterTypes != null && this.ParameterTypes.Count > 0)
            {
                XmlNode sqlParamNodes = XmlDataUtil.AddElement(ref xDoc, relationRuleNode, "ParameterTypes");
                foreach (string paramName in this.ParameterTypes.Keys)
                {
                    XmlNode paramNode = XmlDataUtil.AddElement(ref xDoc, sqlParamNodes, "InputParameter");
                    XmlDataUtil.UpdateAttribute(ref xDoc, paramNode, "ParameterName", paramName);
                    XmlDataUtil.UpdateAttribute(ref xDoc, paramNode, "DataType", this.ParameterTypes[paramName].ToString());
                }
            }

            if(this.FieldTransformations !=null && this.FieldTransformations.Count>0)
            {
                XmlNode transformNodes = XmlDataUtil.AddElement(ref xDoc, relationRuleNode, "FieldTransformations");
                foreach(string fieldName in this.FieldTransformations.Keys)
                {
                    XmlNode transformNode = XmlDataUtil.AddElement(ref xDoc, transformNodes, "FieldTransformation");
                    XmlDataUtil.UpdateAttribute(ref xDoc, transformNode, "FieldName",fieldName);
                    XmlDataUtil.UpdateAttribute(ref xDoc, transformNode, "Code",this.FieldTransformations[fieldName]);
                }
            }

            if(this.OutputFieldNameMappings !=null && this.OutputFieldNameMappings.Count>0)
            {
                XmlNode outputMappingNodes = XmlDataUtil.AddElement(ref xDoc, relationRuleNode, "OutputMappings");
                foreach(string colName in this.OutputFieldNameMappings.Keys)
                {
                    XmlNode outputMappingNode = XmlDataUtil.AddElement(ref xDoc, outputMappingNodes, "OutputMapping");
                    XmlDataUtil.UpdateAttribute(ref xDoc, outputMappingNode, "ColName", colName);
                    XmlDataUtil.UpdateAttribute(ref xDoc, outputMappingNode, "TgtField", this.OutputFieldNameMappings[colName]);
                }
            }

            if(this.LinkCalcArgumentMappings !=null && this.LinkCalcArgumentMappings.Count>0)
            {
                XmlNode argMappingNodes = XmlDataUtil.AddElement(ref xDoc, relationRuleNode, "LinkCalcArgMappings");
                foreach(string argName in this.LinkCalcArgumentMappings.Keys)
                {
                    XmlNode argMappingNode = XmlDataUtil.AddElement(ref xDoc, argMappingNodes, "LinkCalcArgMapping");
                    XmlDataUtil.UpdateAttribute(ref xDoc, argMappingNode, "ArgName", argName);
                    ArgumentFieldBinding argBinding = this.LinkCalcArgumentMappings[argName];
                    XmlDataUtil.UpdateAttribute(ref xDoc, argMappingNode, "FieldName", argBinding.PropertyName);
                    XmlDataUtil.UpdateAttribute(ref xDoc, argMappingNode, "FromSrcEntity",argBinding.FromSrcEntity.ToString());
                    XmlDataUtil.UpdateAttribute(ref xDoc, argMappingNode, "FromTgtEntity",argBinding.FromTgtEntity.ToString());
                }
            }

            XmlDataUtil.UpdateAttribute(ref xDoc, relationRuleNode,"EntityLinkerType",this.EntityLinkerType.FullName);
            XmlDataUtil.UpdateAttribute(ref xDoc, relationRuleNode,"LinkDistanceThreshold",this.LinkDistanceThreshold.ToString());
            XmlDataUtil.UpdateAttribute(ref xDoc, relationRuleNode,"AllowLoopBack",this.AllowLoopBack.ToString());
            XmlDataUtil.UpdateAttribute(ref xDoc, relationRuleNode, "HopDampFactor", this.HopDampFactor.ToString());

            XmlDataUtil.UpdateAttribute(ref xDoc, relationRuleNode, "DedupCalcType",this.DedupCalculatorType.FullName);
            if(this.DedupNativeComparers !=null && this.DedupNativeComparers.Count>0)
            {
                XmlNode nativeComparerNodes = XmlDataUtil.AddElement(ref xDoc, relationRuleNode, "NativeComparers");
                foreach(string fieldName in this.DedupNativeComparers.Keys)
                {
                    XmlNode nativeComparerNode = XmlDataUtil.AddElement(ref xDoc, nativeComparerNodes, "NativeComparer");
                    XmlDataUtil.UpdateAttribute(ref xDoc, nativeComparerNode, "FieldName", fieldName);
                    XmlDataUtil.UpdateAttribute(ref xDoc, nativeComparerNode, "Comparer", this.DedupNativeComparers[fieldName]);
                }
            }
            if(this.DedupAssociativeComparers !=null && this.DedupAssociativeComparers.Count>0)
            {
                XmlNode associativeNodes = XmlDataUtil.AddElement(ref xDoc, relationRuleNode, "AssociativeComparers");
                foreach(string fieldName in this.DedupAssociativeComparers.Keys)
                {
                    XmlNode associativeNode = XmlDataUtil.AddElement(ref xDoc, associativeNodes, "AssociativeComparer");
                    XmlDataUtil.UpdateAttribute(ref xDoc, associativeNode, "FieldName", fieldName);
                    XmlDataUtil.UpdateAttribute(ref xDoc, associativeNode, "Comparer", this.DedupAssociativeComparers[fieldName]);
                }
            }
            if(this.DedupFieldWeights !=null && this.DedupFieldWeights.Count>0)
            {
                XmlNode fieldWeightNodes = XmlDataUtil.AddElement(ref xDoc, relationRuleNode, "DedupFieldWeights");
                foreach(string fieldName in this.DedupFieldWeights.Keys)
                {
                    XmlNode fieldNode = XmlDataUtil.AddElement(ref xDoc, fieldWeightNodes, "Field");
                    XmlDataUtil.UpdateAttribute(ref xDoc, fieldNode, "FieldName",fieldName);
                    XmlDataUtil.UpdateAttribute(ref xDoc, fieldNode, "Weight",this.DedupFieldWeights[fieldName].ToString());
                }
            }
            if(this.DedupPenalties !=null && this.DedupPenalties.Count>0)
            {
                XmlNode penaltyNodes = XmlDataUtil.AddElement(ref xDoc, relationRuleNode, "Penalties");
                foreach(string fieldName in this.DedupPenalties.Keys)
                {
                    XmlNode penaltyNode = XmlDataUtil.AddElement(ref xDoc, penaltyNodes, "Penalty");
                    XmlDataUtil.UpdateAttribute(ref xDoc, penaltyNode, "FieldName", fieldName);
                    XmlDataUtil.UpdateAttribute(ref xDoc, penaltyNode, "Penalty", this.DedupPenalties[fieldName].ToString());
                }
            }
            XmlDataUtil.UpdateAttribute(ref xDoc, relationRuleNode, "DedupSimilarityThreshold", this.DedupSimilarityThreshold.ToString());
            XmlDataUtil.UpdateAttribute(ref xDoc, relationRuleNode, "DedupAsociationThreshold", this.DedupAssociationThreshold.ToString());
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="relationRuleNode"></param>
        /// <returns></returns>
        public static EntityRelationRule ReadRelationRule(XmlNode relationRuleNode)
        {
            Assembly assembly = typeof(EntityRelationRule).Assembly;
            EntityRelationRule rule=new EntityRelationRule();
            rule.RuleName = XmlDataUtil.GetAttributeValue(relationRuleNode, "RuleName", "");
            rule.SrcEntityType = assembly.GetType(XmlDataUtil.GetAttributeValue(relationRuleNode, "SrcEntityType", ""));
            rule.SrcEntityIDField = XmlDataUtil.GetAttributeValue(relationRuleNode, "SrcIDField", "");
            rule.SrcEntityNameField = XmlDataUtil.GetAttributeValue(relationRuleNode, "SrcNameField", "");
            rule.TgtEntityType = assembly.GetType(XmlDataUtil.GetAttributeValue(relationRuleNode, "TgtEntityType", ""));
            rule.TgtEntityIDField = XmlDataUtil.GetAttributeValue(relationRuleNode, "TgtIDField", "");
            rule.TgtEntityNameField = XmlDataUtil.GetAttributeValue(relationRuleNode, "TgtNameField", "");

            rule.DbProvider = (DataProviderType) Enum.Parse(
                                                     typeof (DataProviderType),
                                                     XmlDataUtil.GetAttributeValue(
                                                         relationRuleNode, "DbProvider",
                                                         DataProviderType.MSSQL.ToString()));
            rule.ConnStr = XmlDataUtil.GetAttributeValue(relationRuleNode, "ConnStr", "");
            rule.Sql = XmlDataUtil.GetAttributeValue(relationRuleNode, "Sql", "");
            rule.ParameterTypes=new Dictionary<string, SqlDbType>();

            rule.FieldNameMappings = new Dictionary<string, string>();
            XmlNodeList inputMappingNodes = relationRuleNode.SelectNodes("InputMappings/InputMapping");
            if (inputMappingNodes != null && inputMappingNodes.Count > 0)
            {
                foreach (XmlNode inputMappingNode in inputMappingNodes)
                {
                    string srcField = XmlDataUtil.GetAttributeValue(inputMappingNode, "SrcField", "");
                    string propName = XmlDataUtil.GetAttributeValue(inputMappingNode, "PropName", "");
                    rule.FieldNameMappings.Add(propName, srcField);
                }
            }

            rule.SqlParameters=new Dictionary<string, string>();
            XmlNodeList paramNodes = relationRuleNode.SelectNodes("SqlParameters/SqlParameter");
            if(paramNodes !=null && paramNodes.Count>0)
            {
                foreach(XmlNode paramNode in paramNodes)
                {
                    string propName = XmlDataUtil.GetAttributeValue(paramNode, "PropertyName", "");
                    string paramName = XmlDataUtil.GetAttributeValue(paramNode, "ParameterName", "");
                    rule.SqlParameters.Add(propName, paramName);
                }
            }

            rule.ParameterTypes=new Dictionary<string, SqlDbType>();
            XmlNodeList inputParamNodes = relationRuleNode.SelectNodes("ParameterTypes/InputParameter");
            if(inputParamNodes !=null && inputParamNodes.Count>0)
            {
                foreach(XmlNode inputParamNode in inputParamNodes)
                {
                    string srcField = XmlDataUtil.GetAttributeValue(inputParamNode, "ParameterName", "");
                    SqlDbType dbType = (SqlDbType) Enum.Parse(typeof (SqlDbType),
                        XmlDataUtil.GetAttributeValue(inputParamNode, "DataType", SqlDbType.VarChar.ToString()));
                    rule.ParameterTypes.Add(srcField, dbType);
                }
            }
            
            rule.FieldTransformations=new Dictionary<string, string>();
            XmlNodeList transformNodes = relationRuleNode.SelectNodes("FieldTransformations/FieldTransformation");
            if(transformNodes !=null && transformNodes.Count>0)
            {
                foreach(XmlNode transformNode in transformNodes)
                {
                    string fieldName = XmlDataUtil.GetAttributeValue(transformNode, "FieldName", "");
                    string code = XmlDataUtil.GetAttributeValue(transformNode, "Code", "");
                    rule.FieldTransformations.Add(fieldName, code);
                }
            }

            rule.OutputFieldNameMappings=new Dictionary<string, string>();
            XmlNodeList outputMappingNodes = relationRuleNode.SelectNodes("OutputMappings/OutputMapping");
            if(outputMappingNodes !=null && outputMappingNodes.Count>0)
            {
                foreach(XmlNode outputMappingNode in outputMappingNodes)
                {
                    string colName = XmlDataUtil.GetAttributeValue(outputMappingNode, "ColName", "");
                    string tgtField = XmlDataUtil.GetAttributeValue(outputMappingNode, "TgtField", "");
                    rule.OutputFieldNameMappings.Add(colName,tgtField);
                }
            }

            rule.LinkCalcArgumentMappings=new Dictionary<string, ArgumentFieldBinding>();
            XmlNodeList argMappingNodes = relationRuleNode.SelectNodes("LinkCalcArgMappings/LinkCalcArgMapping");
            if(argMappingNodes !=null && argMappingNodes.Count>0)
            {
                foreach(XmlNode argMappingNode in argMappingNodes)
                {
                    string argName = XmlDataUtil.GetAttributeValue(argMappingNode, "ArgName", "");
                    string propName = XmlDataUtil.GetAttributeValue(argMappingNode, "FieldName", "");
                    bool fromSrcEntity =
                        bool.Parse(XmlDataUtil.GetAttributeValue(argMappingNode, "FromSrcEntity", false.ToString()));
                    bool fromTgtEntity =
                        bool.Parse(XmlDataUtil.GetAttributeValue(argMappingNode, "FromTgtEntity", true.ToString()));
                    ArgumentFieldBinding argBinding=new ArgumentFieldBinding();
                    argBinding.ArgumentName = argName;
                    argBinding.PropertyName = propName;
                    argBinding.FromSrcEntity = fromSrcEntity;
                    argBinding.FromTgtEntity = fromTgtEntity;
                    rule.LinkCalcArgumentMappings.Add(argName, argBinding);
                }
            }

            rule.EntityLinkerType =
                assembly.GetType(XmlDataUtil.GetAttributeValue(relationRuleNode, "EntityLinkerType", ""));
            rule.LinkDistanceThreshold = double.Parse(
                XmlDataUtil.GetAttributeValue(relationRuleNode, "LinkDistanceThreshold", "1"));
            rule.AllowLoopBack = bool.Parse(
                XmlDataUtil.GetAttributeValue(relationRuleNode, "AllowLoopBack", false.ToString()));
            rule.HopDampFactor = double.Parse(
                XmlDataUtil.GetAttributeValue(relationRuleNode, "HopDampFactor", "0.5"));
            
            rule.DedupCalculatorType = assembly.GetType(
                XmlDataUtil.GetAttributeValue(relationRuleNode, "DedupCalcType", ""));
            rule.DedupNativeComparers=new Dictionary<string, string>();
            XmlNodeList nativeNodes = relationRuleNode.SelectNodes("NativeComparers/NativeComparer");
            if(nativeNodes !=null && nativeNodes.Count>0)
            {
                foreach(XmlNode nativeNode in nativeNodes)
                {
                    string fieldName = XmlDataUtil.GetAttributeValue(nativeNode, "FieldName", "");
                    string comparerName = XmlDataUtil.GetAttributeValue(nativeNode, "Comparer", "");
                    rule.DedupNativeComparers.Add(fieldName, comparerName);
                }
            }
            rule.DedupAssociativeComparers=new Dictionary<string, string>();
            XmlNodeList assoNodes = relationRuleNode.SelectNodes("AssociativeComparers/AssociativeComparer");
            if(assoNodes !=null && assoNodes.Count>0)
            {
                foreach(XmlNode associativeNode in assoNodes)
                {
                    string fieldName = XmlDataUtil.GetAttributeValue(associativeNode, "FieldName", "");
                    string comparerName = XmlDataUtil.GetAttributeValue(associativeNode, "Comparer", "");
                    rule.DedupAssociativeComparers.Add(fieldName, comparerName);
                }
            }
            rule.DedupFieldWeights = new Dictionary<string, double>();
            XmlNodeList fieldWeightNodes = relationRuleNode.SelectNodes("DedupFieldWeights/Field");
            if (fieldWeightNodes != null && fieldWeightNodes.Count > 0)
            {
                foreach (XmlNode fieldNode in fieldWeightNodes)
                {
                    string fieldName = XmlDataUtil.GetAttributeValue(fieldNode, "FieldName", "");
                    double weight = double.Parse(XmlDataUtil.GetAttributeValue(fieldNode, "Weight", "1"));
                    rule.DedupFieldWeights.Add(fieldName, weight);
                }
            }
            rule.DedupPenalties=new Dictionary<string, double>();
            XmlNodeList penaltyNodes = relationRuleNode.SelectNodes("Penalties/Penalty");
            if(penaltyNodes !=null && penaltyNodes.Count>0)
            {
                foreach(XmlNode penaltyNode in penaltyNodes)
                {
                    string fieldName = XmlDataUtil.GetAttributeValue(penaltyNode, "FieldName", "");
                    double penalty = double.Parse(XmlDataUtil.GetAttributeValue(penaltyNode, "Penalty", "0"));
                    rule.DedupPenalties.Add(fieldName,penalty);
                }
            }

            rule.DedupAssociationThreshold = double.Parse(
                XmlDataUtil.GetAttributeValue(relationRuleNode, "DedupAssociationThreshold", "0"));
            rule.DedupSimilarityThreshold = double.Parse(
                XmlDataUtil.GetAttributeValue(relationRuleNode, "DedupSimilarityThreshold", "1"));
            

            return rule;
        }
        #endregion
    }
}
